# `suppressPackageStartupMessages` is a function in R that suppresses the startup messages that are generated when loading a package. It can be useful to prevent the console from being cluttered with messages when you are loading multiple packages or when you don't want to see the package loading messages.
shhh <- suppressPackageStartupMessages # It's a library, so shhh!


shhh(library( mgcv ))
shhh(library(dplyr))
shhh(library(ggplot2))
shhh(library(lme4))
shhh(library(tidymv))
shhh(library(gamlss))
shhh(library(gsubfn))
shhh(library(lmerTest))
shhh(library(tidyverse))
shhh(library(boot))
shhh(library(rsample))
shhh(library(plotrix))
shhh(library(ggrepel))
shhh(library(mgcv))

# `theme_set(theme_bw())` is a command in R that sets the default theme for all subsequent plots to a theme called `theme_bw()`.
# `theme_bw()` is a built-in theme in the ggplot2 package that provides a classic black-and-white theme with white grid lines. By setting the default theme to `theme_bw()`, all plots that are created using ggplot2 will have this theme unless a different theme is explicitly specified.
theme_set(theme_bw())

# `options(digits=4)` is a command in R that sets the number of digits to display when printing numeric values.

# By default, R will print up to 7 digits for numeric values. However, you can use the `options()` function to change this behavior. In this case, `options(digits=4)` sets the number of digits to 4, which means that all numeric values will be displayed with at most 4 digits.
options(digits=4)
set.seed(444)

# The `pipe_message` function first outputs the status message using the message() function. This can be useful for tracking the progress of a long computation or for providing information to the user.
# After printing the message, the function returns the .data argument unchanged. This allows the function to be used as a "pipe" in a data processing pipeline, where the output of one function is used as the input to the next function.
pipe_message = function(.data, status) {message(status); .data}

Read in MoTR Data

# Interesting way of reading multiple files in one directory!

file_prefix = "../reading_measures/cleaned_f160_b40"
fnames = list.files(path=file_prefix)
# fnames: [1] "reader_55_reading_measures.csv" "reader_56_reading_measures.csv"
# [3] "reader_59_reading_measures.csv" "reader_61_reading_measures.csv"

# Read in the data
df = data.frame()

# uses the mutate() function from the dplyr package to create a new column in a data frame called subj. The new column is created by removing the substring _reading_measures.csv from the values in an existing column called f.
for (f in fnames) {
  temp = read.csv(paste0(file_prefix, "/", f)) %>%
    mutate(subj = str_remove(f, "_reading_measures.csv")) %>%
    dplyr::select(expr_id, cond_id, para_nr, word, word_nr, first_duration, total_duration,
                  gaze_duration, go_pass_time, FPReg, subj) %>%
    rename(go_past_time = go_pass_time)
  df = rbind(df, temp)
}
df

# `gather will take column 6-9's column name make it rows, name 'metric', take their value as another row`
motr_df = df %>%
  mutate(expr_id = if_else(expr_id == 1, "Attachment", "Provo")) %>%
  gather(metric, value, 6:10)

motr_df
NA
# Average across subjects
motr_agg_df = motr_df %>%
  group_by(expr_id, cond_id, para_nr, word, word_nr, metric) %>%
    summarise(value = mean(value)) %>%    # sum up four subjects for each metric and divide by 4.
  ungroup() %>%
  arrange(expr_id, cond_id, para_nr, word_nr)   # like sort in python 
`summarise()` has grouped output by 'expr_id', 'cond_id', 'para_nr', 'word', 'word_nr'. You can override using the `.groups` argument.
#View(motr_agg_df)

motr_agg_df

table(motr_df$subj)

reader_59 reader_61 reader_62 reader_63 
     3100      3100      3100      3100 
# reader_55 reader_56 reader_59 reader_61 
#    3100      3100      3100      3100

motr_provo_df = motr_agg_df %>%
  filter(expr_id == "Provo") %>%
  rename(text_id = para_nr,
         word_text_idx = word_nr,
         motr_value = value) %>%
  dplyr::select(-expr_id, -cond_id)

motr_provo_df
NA

Comparison to Provo

# Read in Provo surprisal, frequency and length data
provo_modeling_df = read.csv("../ancillary_data/provo_df.csv") %>%
  dplyr::select(text_id, sent_id, trigger_idx, word, freq, surp, len) %>%
  rename(word_idx = trigger_idx)

provo_modeling_df
NA
# Read in Provo eyetracking data

provo_raw_df = read.csv("../ancillary_data/provo_eyetracking.csv")

provo_eyetracking_df = provo_raw_df %>%
  dplyr::select(Participant_ID, Text_ID, Sentence_Number, Word_In_Sentence_Number, IA_ID, Word,IA_FIRST_FIXATION_DURATION, IA_FIRST_FIX_PROGRESSIVE, IA_FIRST_RUN_DWELL_TIME, IA_DWELL_TIME, IA_REGRESSION_PATH_DURATION, IA_REGRESSION_OUT) %>%
  rename( first_duration = IA_FIRST_FIXATION_DURATION,    # whether it is first pass?
          gaze_duration = IA_FIRST_RUN_DWELL_TIME,
          total_duration = IA_DWELL_TIME,
          go_past_time = IA_REGRESSION_PATH_DURATION,
          FPReg = IA_REGRESSION_OUT,
          subj = Participant_ID,
          text_id = Text_ID,
          sent_id = Sentence_Number,
          word_idx = Word_In_Sentence_Number,
          word_text_idx = IA_ID,
          word = Word,
          ff_progressive = IA_FIRST_FIX_PROGRESSIVE) %>% # notice:average across subj, binary(0,1) becomes float.
  mutate(gaze_duration = ifelse(ff_progressive == 0, 0, gaze_duration),
         go_past_time = ifelse(ff_progressive == 0, 0, go_past_time)) %>%
  dplyr::select(-ff_progressive) %>%
  gather(metric, value, 7:11) %>%      # not include FFReg which is in column 11
  mutate(value = if_else(is.na(value), as.integer(0), as.integer(value))) %>%
  drop_na() %>%   # actually, drop first word in a sentence
  group_by(text_id, word_text_idx, sent_id, word_idx, word, metric) %>%
  summarise(value = mean(value)) %>%
  ungroup()
`summarise()` has grouped output by 'text_id', 'word_text_idx', 'sent_id', 'word_idx', 'word'. You can override using the `.groups` argument.
# View(provo_eyetracking_df)
provo_eyetracking_df
NA
provo_df = merge(provo_eyetracking_df, provo_modeling_df, by=c("text_id", "sent_id", "word_idx")) %>%
  mutate(word_text_idx = as.integer(word_text_idx - 1)) %>%
  arrange(text_id, sent_id, word_idx)
provo_df

provo_df = merge(provo_df, motr_provo_df, by=c("text_id", "word_text_idx", "metric")) %>%
  rename(eyetr_value = value) %>%
  arrange(text_id, sent_id, word_idx) %>%
  filter(word.x == word) %>%      #word.y has no captical word
  dplyr::select(-word.x, -word.y) %>%
  mutate(motr_outlier = if_else(motr_value > (mean(motr_value) + 3 * sd(motr_value) ), T, F)) %>%
  filter(motr_outlier == F) %>%     # clear outlier -> 13 was filtered.
  gather(measure, value, c("eyetr_value", "motr_value"))  %>%
  # filter(metric != "first_duration") %>%
  filter(metric != "FPReg")

# View(provo_df)
provo_df %>%
  ggplot(aes(x = value, color=metric)) +
    geom_density() +
    facet_wrap(.~measure) +
    xlab("Reading Measure")

# ggsave("../visualization/density.png", device = "png", width = 6, height = 2.5)
provo_df %>%
  filter(measure == "motr_value") %>%
  ggplot(aes(x = value, color=metric)) +
    geom_density() +
    xlab("Reading Measure") +
    ggtitle("Density plot of value for motr")

provo_df %>%
  filter(measure == "eyetr_value") %>%
  ggplot(aes(x = value, color=metric)) +
    geom_density() +
    xlab("Reading Measure") +
    ggtitle("Density plot of value for eyetr")

gd_df = provo_df %>% filter(metric == "total_duration") %>% spread(measure, value)
View(gd_df)

cor.test(gd_df$eyetr_value, gd_df$motr_value)          #cor rise from 0.529 to 0.5989 

    Pearson's product-moment correlation

data:  gd_df$eyetr_value and gd_df$motr_value
t = 16, df = 375, p-value <2e-16
alternative hypothesis: true correlation is not equal to 0
95 percent confidence interval:
 0.5721 0.6928
sample estimates:
   cor 
0.6364 
provo_df %>%
  spread(measure, value) %>%
  ggplot(aes(x = motr_value, y=eyetr_value, color=metric)) +
    geom_point(alpha = 0.2) +
    facet_wrap(.~metric, scales="free") +
    geom_smooth()


# ggsave("../visualization/metric_cor.png", device = "png", width = 6, height = 2.5)
gd_df_2 = provo_df %>% filter(metric == "first_duration") %>% spread(measure, value)

cor.test(gd_df_2$eyetr_value, gd_df_2$motr_value)

    Pearson's product-moment correlation

data:  gd_df_2$eyetr_value and gd_df_2$motr_value
t = 15, df = 378, p-value <2e-16
alternative hypothesis: true correlation is not equal to 0
95 percent confidence interval:
 0.5577 0.6812
sample estimates:
   cor 
0.6233 
gd_df_3 = provo_df %>% filter(metric == "gaze_duration") %>% spread(measure, value)

cor.test(gd_df_3$eyetr_value, gd_df_3$motr_value)

    Pearson's product-moment correlation

data:  gd_df_3$eyetr_value and gd_df_3$motr_value
t = 17, df = 378, p-value <2e-16
alternative hypothesis: true correlation is not equal to 0
95 percent confidence interval:
 0.6008 0.7145
sample estimates:
   cor 
0.6614 
gd_df_4 = provo_df %>% filter(metric == "go_past_time") %>% spread(measure, value)

cor.test(gd_df_4$eyetr_value, gd_df_4$motr_value)

    Pearson's product-moment correlation

data:  gd_df_4$eyetr_value and gd_df_4$motr_value
t = 13, df = 370, p-value <2e-16
alternative hypothesis: true correlation is not equal to 0
95 percent confidence interval:
 0.4757 0.6178
sample estimates:
   cor 
0.5507 
provo_df %>%
  gather(word_prop, word_prop_val, c("freq", "len", "surp")) %>%
  filter(metric == "gaze_duration") %>%
  ggplot(aes(x = value, y=word_prop_val, color = measure)) +
    geom_point(alpha = 0.2) +
    facet_grid(word_prop~measure, scales="free") +
    geom_smooth() +
    xlab("Reading Measure")


# ggsave("../visualization/word_prop_comps.png", device = "png", width = 6, height = 3)
provo_df %>%
  ggplot(aes(x = value, y=freq, color=metric)) +
    geom_point(alpha = 0.2) +
    facet_grid(metric~measure, scales="free") +
    geom_smooth()

provo_df %>%
  ggplot(aes(x = value, y=surp, color=metric)) +
    geom_point(alpha = 0.2) +
    facet_grid(metric~measure, scales="free") +
    geom_smooth()

Shape of surprisal / RT relationship


fit_gam_inner = function(bootstrap_sample, mean_predictors) {
  
  df = bootstrap_sample$data
  weights = tabulate(as.integer(bootstrap_sample), nrow(df))
  
  m = gam(psychometric ~ s(surp, bs = 'cr', k = 6) + s(prev_surp, bs = 'cr', k = 6) + te(freq, len, bs = 'cr') + te(prev_freq, prev_len, bs = 'cr'), data = df, weights = weights)
  terms_to_predict = c("s(surp)", "s(prev_surp)")
  
  newdata = data.frame(surp=seq(0,20,by=0.1), prev_surp=mean_predictors$surp,
                       freq=mean_predictors$freq, prev_freq=mean_predictors$freq,
                       len=mean_predictors$freq, prev_len=mean_predictors$freq)

  # Returns a matrix N_samples * N_terms.
  per_term_predictions = predict(m, newdata=newdata, terms=terms_to_predict, type="terms")

  # Additive model -- sum across predictor response contributions (matrix columns).
  predictions = rowSums(per_term_predictions)

  return(newdata %>% mutate(y=predictions))
}

fit_gam = function(df, mean_predictors, alpha=0.05) {
  # Bootstrap-resample data
  boot_models = df %>% bootstraps(times=10) %>% 
   # Fit a GAM and get predictions for each sample
    mutate(smoothed=map(splits, fit_gam_inner, mean_predictors=mean_predictors))
  
  # Extract mean and 5% and 95% percentile y-values for each surprisal value
  result = boot_models %>% 
    unnest(smoothed) %>% 
    dplyr::select(surp, y) %>% 
    group_by(surp) %>% 
      summarise(y_lower=quantile(y, alpha / 2), 
                y_upper=quantile(y, 1 - alpha / 2),
                y=mean(y)) %>% 
    ungroup()
  
  return (result)
}

gam_modeling_df = provo_df %>%
  spread(measure, value) %>%
  mutate(len = nchar(word)) %>%
  group_by(metric, text_id) %>%
    arrange(word_text_idx) %>%
    mutate(prev_surp = lag(surp),
           prev_freq = lag(freq),
           prev_len = lag(len),
           prev_eyetr_value = lag(eyetr_value)) %>%
  ungroup() %>%
  drop_na() %>%
  rename(psychometric = motr_value)


smooths_df = data.frame()

metrics = c("gaze_duration", "total_duration", "go_past_time", "first_duration")
for (m in metrics) {
  print(paste0("Fitting model for ", m))
  dummy_df = gam_modeling_df %>% filter(metric == m)
  mean_predictors = dummy_df %>% summarise(surp = mean(surp), len = mean(len), freq = mean(freq))
  smooths = dummy_df %>% fit_gam(., mean_predictors)
  #Fix 0 surprisal = 0 ms
  gam_smooths = smooths %>% mutate(delta = 0 - y[1], y=y + delta, y_lower= y_lower + delta, y_upper=y_upper + delta)
  smooths_df = rbind(smooths_df, gam_smooths %>% mutate(psychometric = m))
}
[1] "Fitting model for gaze_duration"
[1] "Fitting model for total_duration"
[1] "Fitting model for go_past_time"
[1] "Fitting model for first_duration"

# Surprisal curves
  ggplot() +
      geom_line(data = smooths_df, aes(x=surp, y=y, color = psychometric), size=0.7) +
      geom_ribbon(data = smooths_df, aes(x=surp, ymin=y_lower, ymax=y_upper, fill = psychometric), alpha=0.3, size=0.5) +
      scale_x_continuous(labels=c(0, 10, 20), breaks=c(0, 10, 20), minor_breaks = NULL) +
      facet_wrap(psychometric~.) +
      ylab("Slowdown due to Surprisal (ms)") +
      xlab("Surprisal of Word") +
      ggtitle("Relationship between MoTR Times and Surprisal")

  theme(
    legend.position = "none",
    panel.grid.minor = element_blank()
  )
List of 2
 $ legend.position : chr "none"
 $ panel.grid.minor: list()
  ..- attr(*, "class")= chr [1:2] "element_blank" "element"
 - attr(*, "class")= chr [1:2] "theme" "gg"
 - attr(*, "complete")= logi FALSE
 - attr(*, "validate")= logi TRUE

Targeted Evaluation Data


motr_attach_df = motr_df %>%
  filter(expr_id == "Attachment") %>%
  rename( item_id = para_nr) %>%
  mutate(item_id = as.integer(item_id)) %>%
  mutate(cond_id = as.factor(cond_id)) %>%
  mutate(cond_id = if_else(cond_id == 1, "No Comma", "Comma")) %>%
  filter(metric != "FPReg") %>%

  filter(! (item_id == 4 & cond_id == "No Comma") ) %>% # just because of alignment issues for now
  
  mutate(crit = if_else(word == "and", word_nr, as.integer(0) )) %>%
  group_by(cond_id, item_id) %>%
    mutate(crit = unique(crit)[2]) %>%
  ungroup() %>%
  mutate(word_nr = word_nr - crit)


agg_motr_attach_df = motr_attach_df %>%
  filter(word_nr >= -2, word_nr < 6) %>%
  group_by(cond_id, word_nr, metric) %>%
    summarise( m = mean(value),
               sd = std.error(value),
               upper = m + 1.96 * sd,
               lower = m - 1.98 * sd,
               n = n()) %>%
  ungroup()
`summarise()` has grouped output by 'cond_id', 'word_nr'. You can override using the `.groups` argument.
agg_motr_attach_df %>%
  ggplot(aes(x = word_nr, y = m, color = cond_id)) +
    geom_rect(aes(xmin = 2.5, xmax = 5.5, ymin = 100, ymax = 800), fill=alpha("white", 0), color = "#45ef70", linetype = "dotted") +
    geom_point() +
    geom_errorbar(aes(ymax = upper, ymin = lower), width = 0.3) +
    geom_line() +
    #geom_text(aes(label = word, y = if_else(cond_id == "Comma", 3000, 3500)), size = 2) +
    #facet_grid(para_nr~cond_id) +
  ylab("Reading Time") +
  xlab("Condition") +
  scale_x_continuous(breaks=-2:5, labels=c("the", "man", "and", "his", "wife", "ran", "away", "from")) +
  facet_grid(~metric) +
  theme(
    legend.position = "bottom",
    axis.text.x = element_text(angle = 45, hjust = 1)
  )


#ggsave("../visualization/attachment.png", device = "png", width = 6, height = 3)

options(JULIA_HOME = "/Applications/Julia-1.8.app/Contents/Resources/julia/bin/")
# library(jglmm)
# jglmm_setup()

attach_lm_df = motr_attach_df %>%
  filter(metric == "gaze_duration") %>%
  filter(word_nr == 3) %>%
  mutate(item_id = as.factor(item_id),
         subj = as.factor(subj))

m = attach_lm_df %>%
  lmer(value ~ cond_id + (cond_id | item_id) + (cond_id | subj), data=.)
boundary (singular) fit: see help('isSingular')
Warning: Model failed to converge with 1 negative eigenvalue: -1.6e-03
summary(m)
Linear mixed model fit by REML. t-tests use Satterthwaite's method ['lmerModLmerTest']
Formula: value ~ cond_id + (cond_id | item_id) + (cond_id | subj)
   Data: .

REML criterion at convergence: 825.2

Scaled residuals: 
   Min     1Q Median     3Q    Max 
-1.121 -0.984 -0.205  0.638  3.783 

Random effects:
 Groups   Name            Variance Std.Dev. Corr
 item_id  (Intercept)     1.44e+03 3.79e+01     
          cond_idNo Comma 1.74e+02 1.32e+01 1.00
 subj     (Intercept)     0.00e+00 0.00e+00     
          cond_idNo Comma 2.27e-12 1.51e-06  NaN
 Residual                 7.70e+04 2.77e+02     
Number of obs: 60, groups:  item_id, 15; subj, 4

Fixed effects:
                Estimate Std. Error    df t value Pr(>|t|)    
(Intercept)        282.7       50.8   7.0    5.56  0.00085 ***
cond_idNo Comma     23.7       75.5  12.6    0.31  0.75882    
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Correlation of Fixed Effects:
            (Intr)
cond_idNCmm -0.673
optimizer (nloptwrap) convergence code: 0 (OK)
boundary (singular) fit: see help('isSingular')
LS0tCnRpdGxlOiAiRXhwbG9yYXRvcnkgQW5hbHlzaXMgZm9yIE1vVFIgUmVhZGluZyBEYXRhIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tCgpgYGB7cn0KIyBgc3VwcHJlc3NQYWNrYWdlU3RhcnR1cE1lc3NhZ2VzYCBpcyBhIGZ1bmN0aW9uIGluIFIgdGhhdCBzdXBwcmVzc2VzIHRoZSBzdGFydHVwIG1lc3NhZ2VzIHRoYXQgYXJlIGdlbmVyYXRlZCB3aGVuIGxvYWRpbmcgYSBwYWNrYWdlLiBJdCBjYW4gYmUgdXNlZnVsIHRvIHByZXZlbnQgdGhlIGNvbnNvbGUgZnJvbSBiZWluZyBjbHV0dGVyZWQgd2l0aCBtZXNzYWdlcyB3aGVuIHlvdSBhcmUgbG9hZGluZyBtdWx0aXBsZSBwYWNrYWdlcyBvciB3aGVuIHlvdSBkb24ndCB3YW50IHRvIHNlZSB0aGUgcGFja2FnZSBsb2FkaW5nIG1lc3NhZ2VzLgpzaGhoIDwtIHN1cHByZXNzUGFja2FnZVN0YXJ0dXBNZXNzYWdlcyAjIEl0J3MgYSBsaWJyYXJ5LCBzbyBzaGhoIQoKCnNoaGgobGlicmFyeSggbWdjdiApKQpzaGhoKGxpYnJhcnkoZHBseXIpKQpzaGhoKGxpYnJhcnkoZ2dwbG90MikpCnNoaGgobGlicmFyeShsbWU0KSkKc2hoaChsaWJyYXJ5KHRpZHltdikpCnNoaGgobGlicmFyeShnYW1sc3MpKQpzaGhoKGxpYnJhcnkoZ3N1YmZuKSkKc2hoaChsaWJyYXJ5KGxtZXJUZXN0KSkKc2hoaChsaWJyYXJ5KHRpZHl2ZXJzZSkpCnNoaGgobGlicmFyeShib290KSkKc2hoaChsaWJyYXJ5KHJzYW1wbGUpKQpzaGhoKGxpYnJhcnkocGxvdHJpeCkpCnNoaGgobGlicmFyeShnZ3JlcGVsKSkKc2hoaChsaWJyYXJ5KG1nY3YpKQoKIyBgdGhlbWVfc2V0KHRoZW1lX2J3KCkpYCBpcyBhIGNvbW1hbmQgaW4gUiB0aGF0IHNldHMgdGhlIGRlZmF1bHQgdGhlbWUgZm9yIGFsbCBzdWJzZXF1ZW50IHBsb3RzIHRvIGEgdGhlbWUgY2FsbGVkIGB0aGVtZV9idygpYC4KIyBgdGhlbWVfYncoKWAgaXMgYSBidWlsdC1pbiB0aGVtZSBpbiB0aGUgZ2dwbG90MiBwYWNrYWdlIHRoYXQgcHJvdmlkZXMgYSBjbGFzc2ljIGJsYWNrLWFuZC13aGl0ZSB0aGVtZSB3aXRoIHdoaXRlIGdyaWQgbGluZXMuIEJ5IHNldHRpbmcgdGhlIGRlZmF1bHQgdGhlbWUgdG8gYHRoZW1lX2J3KClgLCBhbGwgcGxvdHMgdGhhdCBhcmUgY3JlYXRlZCB1c2luZyBnZ3Bsb3QyIHdpbGwgaGF2ZSB0aGlzIHRoZW1lIHVubGVzcyBhIGRpZmZlcmVudCB0aGVtZSBpcyBleHBsaWNpdGx5IHNwZWNpZmllZC4KdGhlbWVfc2V0KHRoZW1lX2J3KCkpCgojIGBvcHRpb25zKGRpZ2l0cz00KWAgaXMgYSBjb21tYW5kIGluIFIgdGhhdCBzZXRzIHRoZSBudW1iZXIgb2YgZGlnaXRzIHRvIGRpc3BsYXkgd2hlbiBwcmludGluZyBudW1lcmljIHZhbHVlcy4KCiMgQnkgZGVmYXVsdCwgUiB3aWxsIHByaW50IHVwIHRvIDcgZGlnaXRzIGZvciBudW1lcmljIHZhbHVlcy4gSG93ZXZlciwgeW91IGNhbiB1c2UgdGhlIGBvcHRpb25zKClgIGZ1bmN0aW9uIHRvIGNoYW5nZSB0aGlzIGJlaGF2aW9yLiBJbiB0aGlzIGNhc2UsIGBvcHRpb25zKGRpZ2l0cz00KWAgc2V0cyB0aGUgbnVtYmVyIG9mIGRpZ2l0cyB0byA0LCB3aGljaCBtZWFucyB0aGF0IGFsbCBudW1lcmljIHZhbHVlcyB3aWxsIGJlIGRpc3BsYXllZCB3aXRoIGF0IG1vc3QgNCBkaWdpdHMuCm9wdGlvbnMoZGlnaXRzPTQpCnNldC5zZWVkKDQ0NCkKCiMgVGhlIGBwaXBlX21lc3NhZ2VgIGZ1bmN0aW9uIGZpcnN0IG91dHB1dHMgdGhlIHN0YXR1cyBtZXNzYWdlIHVzaW5nIHRoZSBtZXNzYWdlKCkgZnVuY3Rpb24uIFRoaXMgY2FuIGJlIHVzZWZ1bCBmb3IgdHJhY2tpbmcgdGhlIHByb2dyZXNzIG9mIGEgbG9uZyBjb21wdXRhdGlvbiBvciBmb3IgcHJvdmlkaW5nIGluZm9ybWF0aW9uIHRvIHRoZSB1c2VyLgojIEFmdGVyIHByaW50aW5nIHRoZSBtZXNzYWdlLCB0aGUgZnVuY3Rpb24gcmV0dXJucyB0aGUgLmRhdGEgYXJndW1lbnQgdW5jaGFuZ2VkLiBUaGlzIGFsbG93cyB0aGUgZnVuY3Rpb24gdG8gYmUgdXNlZCBhcyBhICJwaXBlIiBpbiBhIGRhdGEgcHJvY2Vzc2luZyBwaXBlbGluZSwgd2hlcmUgdGhlIG91dHB1dCBvZiBvbmUgZnVuY3Rpb24gaXMgdXNlZCBhcyB0aGUgaW5wdXQgdG8gdGhlIG5leHQgZnVuY3Rpb24uCnBpcGVfbWVzc2FnZSA9IGZ1bmN0aW9uKC5kYXRhLCBzdGF0dXMpIHttZXNzYWdlKHN0YXR1cyk7IC5kYXRhfQoKYGBgCgpgYGB7cn0KCmBgYAoKIyBSZWFkIGluIE1vVFIgRGF0YQoKYGBge3J9CiMgSW50ZXJlc3Rpbmcgd2F5IG9mIHJlYWRpbmcgbXVsdGlwbGUgZmlsZXMgaW4gb25lIGRpcmVjdG9yeSEKCmZpbGVfcHJlZml4ID0gIi4uL3JlYWRpbmdfbWVhc3VyZXMvY2xlYW5lZF9mMTYwX2I0MCIKZm5hbWVzID0gbGlzdC5maWxlcyhwYXRoPWZpbGVfcHJlZml4KQojIGZuYW1lczogWzFdICJyZWFkZXJfNTVfcmVhZGluZ19tZWFzdXJlcy5jc3YiICJyZWFkZXJfNTZfcmVhZGluZ19tZWFzdXJlcy5jc3YiCiMgWzNdICJyZWFkZXJfNTlfcmVhZGluZ19tZWFzdXJlcy5jc3YiICJyZWFkZXJfNjFfcmVhZGluZ19tZWFzdXJlcy5jc3YiCgojIFJlYWQgaW4gdGhlIGRhdGEKZGYgPSBkYXRhLmZyYW1lKCkKCiMgdXNlcyB0aGUgbXV0YXRlKCkgZnVuY3Rpb24gZnJvbSB0aGUgZHBseXIgcGFja2FnZSB0byBjcmVhdGUgYSBuZXcgY29sdW1uIGluIGEgZGF0YSBmcmFtZSBjYWxsZWQgc3Viai4gVGhlIG5ldyBjb2x1bW4gaXMgY3JlYXRlZCBieSByZW1vdmluZyB0aGUgc3Vic3RyaW5nIF9yZWFkaW5nX21lYXN1cmVzLmNzdiBmcm9tIHRoZSB2YWx1ZXMgaW4gYW4gZXhpc3RpbmcgY29sdW1uIGNhbGxlZCBmLgpmb3IgKGYgaW4gZm5hbWVzKSB7CiAgdGVtcCA9IHJlYWQuY3N2KHBhc3RlMChmaWxlX3ByZWZpeCwgIi8iLCBmKSkgJT4lCiAgICBtdXRhdGUoc3ViaiA9IHN0cl9yZW1vdmUoZiwgIl9yZWFkaW5nX21lYXN1cmVzLmNzdiIpKSAlPiUKICAgIGRwbHlyOjpzZWxlY3QoZXhwcl9pZCwgY29uZF9pZCwgcGFyYV9uciwgd29yZCwgd29yZF9uciwgZmlyc3RfZHVyYXRpb24sIHRvdGFsX2R1cmF0aW9uLAogICAgICAgICAgICAgICAgICBnYXplX2R1cmF0aW9uLCBnb19wYXNzX3RpbWUsIEZQUmVnLCBzdWJqKSAlPiUKICAgIHJlbmFtZShnb19wYXN0X3RpbWUgPSBnb19wYXNzX3RpbWUpCiAgZGYgPSByYmluZChkZiwgdGVtcCkKfQpkZgoKIyBgZ2F0aGVyIHdpbGwgdGFrZSBjb2x1bW4gNi05J3MgY29sdW1uIG5hbWUgbWFrZSBpdCByb3dzLCBuYW1lICdtZXRyaWMnLCB0YWtlIHRoZWlyIHZhbHVlIGFzIGFub3RoZXIgcm93YAptb3RyX2RmID0gZGYgJT4lCiAgbXV0YXRlKGV4cHJfaWQgPSBpZl9lbHNlKGV4cHJfaWQgPT0gMSwgIkF0dGFjaG1lbnQiLCAiUHJvdm8iKSkgJT4lCiAgZ2F0aGVyKG1ldHJpYywgdmFsdWUsIDY6MTApCgptb3RyX2RmCgpgYGAKCmBgYHtyfQojIEF2ZXJhZ2UgYWNyb3NzIHN1YmplY3RzCm1vdHJfYWdnX2RmID0gbW90cl9kZiAlPiUKICBncm91cF9ieShleHByX2lkLCBjb25kX2lkLCBwYXJhX25yLCB3b3JkLCB3b3JkX25yLCBtZXRyaWMpICU+JQogICAgc3VtbWFyaXNlKHZhbHVlID0gbWVhbih2YWx1ZSkpICU+JSAgICAjIHN1bSB1cCBmb3VyIHN1YmplY3RzIGZvciBlYWNoIG1ldHJpYyBhbmQgZGl2aWRlIGJ5IDQuCiAgdW5ncm91cCgpICU+JQogIGFycmFuZ2UoZXhwcl9pZCwgY29uZF9pZCwgcGFyYV9uciwgd29yZF9ucikgICAjIGxpa2Ugc29ydCBpbiBweXRob24gCgojVmlldyhtb3RyX2FnZ19kZikKCm1vdHJfYWdnX2RmCgp0YWJsZShtb3RyX2RmJHN1YmopCiMgcmVhZGVyXzU1IHJlYWRlcl81NiByZWFkZXJfNTkgcmVhZGVyXzYxIAojICAgIDMxMDAgICAgICAzMTAwICAgICAgMzEwMCAgICAgIDMxMDAKCmBgYAoKYGBge3J9Cgptb3RyX3Byb3ZvX2RmID0gbW90cl9hZ2dfZGYgJT4lCiAgZmlsdGVyKGV4cHJfaWQgPT0gIlByb3ZvIikgJT4lCiAgcmVuYW1lKHRleHRfaWQgPSBwYXJhX25yLAogICAgICAgICB3b3JkX3RleHRfaWR4ID0gd29yZF9uciwKICAgICAgICAgbW90cl92YWx1ZSA9IHZhbHVlKSAlPiUKICBkcGx5cjo6c2VsZWN0KC1leHByX2lkLCAtY29uZF9pZCkKCm1vdHJfcHJvdm9fZGYKCmBgYAoKCgojIENvbXBhcmlzb24gdG8gUHJvdm8KCmBgYHtyfQojIFJlYWQgaW4gUHJvdm8gc3VycHJpc2FsLCBmcmVxdWVuY3kgYW5kIGxlbmd0aCBkYXRhCnByb3ZvX21vZGVsaW5nX2RmID0gcmVhZC5jc3YoIi4uL2FuY2lsbGFyeV9kYXRhL3Byb3ZvX2RmLmNzdiIpICU+JQogIGRwbHlyOjpzZWxlY3QodGV4dF9pZCwgc2VudF9pZCwgdHJpZ2dlcl9pZHgsIHdvcmQsIGZyZXEsIHN1cnAsIGxlbikgJT4lCiAgcmVuYW1lKHdvcmRfaWR4ID0gdHJpZ2dlcl9pZHgpCgpwcm92b19tb2RlbGluZ19kZgoKYGBgCgpgYGB7cn0KIyBSZWFkIGluIFByb3ZvIGV5ZXRyYWNraW5nIGRhdGEKCnByb3ZvX3Jhd19kZiA9IHJlYWQuY3N2KCIuLi9hbmNpbGxhcnlfZGF0YS9wcm92b19leWV0cmFja2luZy5jc3YiKQoKcHJvdm9fZXlldHJhY2tpbmdfZGYgPSBwcm92b19yYXdfZGYgJT4lCiAgZHBseXI6OnNlbGVjdChQYXJ0aWNpcGFudF9JRCwgVGV4dF9JRCwgU2VudGVuY2VfTnVtYmVyLCBXb3JkX0luX1NlbnRlbmNlX051bWJlciwgSUFfSUQsIFdvcmQsSUFfRklSU1RfRklYQVRJT05fRFVSQVRJT04sIElBX0ZJUlNUX0ZJWF9QUk9HUkVTU0lWRSwgSUFfRklSU1RfUlVOX0RXRUxMX1RJTUUsIElBX0RXRUxMX1RJTUUsIElBX1JFR1JFU1NJT05fUEFUSF9EVVJBVElPTiwgSUFfUkVHUkVTU0lPTl9PVVQpICU+JQogIHJlbmFtZSggZmlyc3RfZHVyYXRpb24gPSBJQV9GSVJTVF9GSVhBVElPTl9EVVJBVElPTiwgICAgIyB3aGV0aGVyIGl0IGlzIGZpcnN0IHBhc3M/CiAgICAgICAgICBnYXplX2R1cmF0aW9uID0gSUFfRklSU1RfUlVOX0RXRUxMX1RJTUUsCiAgICAgICAgICB0b3RhbF9kdXJhdGlvbiA9IElBX0RXRUxMX1RJTUUsCiAgICAgICAgICBnb19wYXN0X3RpbWUgPSBJQV9SRUdSRVNTSU9OX1BBVEhfRFVSQVRJT04sCiAgICAgICAgICBGUFJlZyA9IElBX1JFR1JFU1NJT05fT1VULAogICAgICAgICAgc3ViaiA9IFBhcnRpY2lwYW50X0lELAogICAgICAgICAgdGV4dF9pZCA9IFRleHRfSUQsCiAgICAgICAgICBzZW50X2lkID0gU2VudGVuY2VfTnVtYmVyLAogICAgICAgICAgd29yZF9pZHggPSBXb3JkX0luX1NlbnRlbmNlX051bWJlciwKICAgICAgICAgIHdvcmRfdGV4dF9pZHggPSBJQV9JRCwKICAgICAgICAgIHdvcmQgPSBXb3JkLAogICAgICAgICAgZmZfcHJvZ3Jlc3NpdmUgPSBJQV9GSVJTVF9GSVhfUFJPR1JFU1NJVkUpICU+JSAjIG5vdGljZTphdmVyYWdlIGFjcm9zcyBzdWJqLCBiaW5hcnkoMCwxKSBiZWNvbWVzIGZsb2F0LgogIG11dGF0ZShnYXplX2R1cmF0aW9uID0gaWZlbHNlKGZmX3Byb2dyZXNzaXZlID09IDAsIDAsIGdhemVfZHVyYXRpb24pLAogICAgICAgICBnb19wYXN0X3RpbWUgPSBpZmVsc2UoZmZfcHJvZ3Jlc3NpdmUgPT0gMCwgMCwgZ29fcGFzdF90aW1lKSkgJT4lCiAgZHBseXI6OnNlbGVjdCgtZmZfcHJvZ3Jlc3NpdmUpICU+JQogIGdhdGhlcihtZXRyaWMsIHZhbHVlLCA3OjExKSAlPiUgICAgICAjIG5vdCBpbmNsdWRlIEZGUmVnIHdoaWNoIGlzIGluIGNvbHVtbiAxMQogIG11dGF0ZSh2YWx1ZSA9IGlmX2Vsc2UoaXMubmEodmFsdWUpLCBhcy5pbnRlZ2VyKDApLCBhcy5pbnRlZ2VyKHZhbHVlKSkpICU+JQogIGRyb3BfbmEoKSAlPiUgICAjIGFjdHVhbGx5LCBkcm9wIGZpcnN0IHdvcmQgaW4gYSBzZW50ZW5jZQogIGdyb3VwX2J5KHRleHRfaWQsIHdvcmRfdGV4dF9pZHgsIHNlbnRfaWQsIHdvcmRfaWR4LCB3b3JkLCBtZXRyaWMpICU+JQogIHN1bW1hcmlzZSh2YWx1ZSA9IG1lYW4odmFsdWUpKSAlPiUKICB1bmdyb3VwKCkKCiMgVmlldyhwcm92b19leWV0cmFja2luZ19kZikKcHJvdm9fZXlldHJhY2tpbmdfZGYKCmBgYAoKCmBgYHtyfQpwcm92b19kZiA9IG1lcmdlKHByb3ZvX2V5ZXRyYWNraW5nX2RmLCBwcm92b19tb2RlbGluZ19kZiwgYnk9YygidGV4dF9pZCIsICJzZW50X2lkIiwgIndvcmRfaWR4IikpICU+JQogIG11dGF0ZSh3b3JkX3RleHRfaWR4ID0gYXMuaW50ZWdlcih3b3JkX3RleHRfaWR4IC0gMSkpICU+JQogIGFycmFuZ2UodGV4dF9pZCwgc2VudF9pZCwgd29yZF9pZHgpCnByb3ZvX2RmCgpwcm92b19kZiA9IG1lcmdlKHByb3ZvX2RmLCBtb3RyX3Byb3ZvX2RmLCBieT1jKCJ0ZXh0X2lkIiwgIndvcmRfdGV4dF9pZHgiLCAibWV0cmljIikpICU+JQogIHJlbmFtZShleWV0cl92YWx1ZSA9IHZhbHVlKSAlPiUKICBhcnJhbmdlKHRleHRfaWQsIHNlbnRfaWQsIHdvcmRfaWR4KSAlPiUKICBmaWx0ZXIod29yZC54ID09IHdvcmQpICU+JSAgICAgICN3b3JkLnkgaGFzIG5vIGNhcHRpY2FsIHdvcmQKICBkcGx5cjo6c2VsZWN0KC13b3JkLngsIC13b3JkLnkpICU+JQogIG11dGF0ZShtb3RyX291dGxpZXIgPSBpZl9lbHNlKG1vdHJfdmFsdWUgPiAobWVhbihtb3RyX3ZhbHVlKSArIDMgKiBzZChtb3RyX3ZhbHVlKSApLCBULCBGKSkgJT4lCiAgZmlsdGVyKG1vdHJfb3V0bGllciA9PSBGKSAlPiUgICAgICMgY2xlYXIgb3V0bGllciAtPiAxMyB3YXMgZmlsdGVyZWQuCiAgZ2F0aGVyKG1lYXN1cmUsIHZhbHVlLCBjKCJleWV0cl92YWx1ZSIsICJtb3RyX3ZhbHVlIikpICAlPiUKICAjIGZpbHRlcihtZXRyaWMgIT0gImZpcnN0X2R1cmF0aW9uIikgJT4lCiAgZmlsdGVyKG1ldHJpYyAhPSAiRlBSZWciKQoKIyBWaWV3KHByb3ZvX2RmKQoKCmBgYAoKCmBgYHtyfQpwcm92b19kZiAlPiUKICBnZ3Bsb3QoYWVzKHggPSB2YWx1ZSwgY29sb3I9bWV0cmljKSkgKwogICAgZ2VvbV9kZW5zaXR5KCkgKwogICAgZmFjZXRfd3JhcCgufm1lYXN1cmUpICsKICAgIHhsYWIoIlJlYWRpbmcgTWVhc3VyZSIpCiMgZ2dzYXZlKCIuLi92aXN1YWxpemF0aW9uL2RlbnNpdHkucG5nIiwgZGV2aWNlID0gInBuZyIsIHdpZHRoID0gNiwgaGVpZ2h0ID0gMi41KQpgYGAKCmBgYHtyfQpwcm92b19kZiAlPiUKICBmaWx0ZXIobWVhc3VyZSA9PSAibW90cl92YWx1ZSIpICU+JQogIGdncGxvdChhZXMoeCA9IHZhbHVlLCBjb2xvcj1tZXRyaWMpKSArCiAgICBnZW9tX2RlbnNpdHkoKSArCiAgICB4bGFiKCJSZWFkaW5nIE1lYXN1cmUiKSArCiAgICBnZ3RpdGxlKCJEZW5zaXR5IHBsb3Qgb2YgdmFsdWUgZm9yIG1vdHIiKQpgYGAKCmBgYHtyfQpwcm92b19kZiAlPiUKICBmaWx0ZXIobWVhc3VyZSA9PSAiZXlldHJfdmFsdWUiKSAlPiUKICBnZ3Bsb3QoYWVzKHggPSB2YWx1ZSwgY29sb3I9bWV0cmljKSkgKwogICAgZ2VvbV9kZW5zaXR5KCkgKwogICAgeGxhYigiUmVhZGluZyBNZWFzdXJlIikgKwogICAgZ2d0aXRsZSgiRGVuc2l0eSBwbG90IG9mIHZhbHVlIGZvciBleWV0ciIpCmBgYAoKCmBgYHtyfQpnZF9kZiA9IHByb3ZvX2RmICU+JSBmaWx0ZXIobWV0cmljID09ICJ0b3RhbF9kdXJhdGlvbiIpICU+JSBzcHJlYWQobWVhc3VyZSwgdmFsdWUpClZpZXcoZ2RfZGYpCgpjb3IudGVzdChnZF9kZiRleWV0cl92YWx1ZSwgZ2RfZGYkbW90cl92YWx1ZSkgICAgICAgICAgI2NvciByaXNlIGZyb20gMC41MjkgdG8gMC41OTg5IAoKcHJvdm9fZGYgJT4lCiAgc3ByZWFkKG1lYXN1cmUsIHZhbHVlKSAlPiUKICBnZ3Bsb3QoYWVzKHggPSBtb3RyX3ZhbHVlLCB5PWV5ZXRyX3ZhbHVlLCBjb2xvcj1tZXRyaWMpKSArCiAgICBnZW9tX3BvaW50KGFscGhhID0gMC4yKSArCiAgICBmYWNldF93cmFwKC5+bWV0cmljLCBzY2FsZXM9ImZyZWUiKSArCiAgICBnZW9tX3Ntb290aCgpCgojIGdnc2F2ZSgiLi4vdmlzdWFsaXphdGlvbi9tZXRyaWNfY29yLnBuZyIsIGRldmljZSA9ICJwbmciLCB3aWR0aCA9IDYsIGhlaWdodCA9IDIuNSkKCmBgYAoKYGBge3J9CmdkX2RmXzIgPSBwcm92b19kZiAlPiUgZmlsdGVyKG1ldHJpYyA9PSAiZmlyc3RfZHVyYXRpb24iKSAlPiUgc3ByZWFkKG1lYXN1cmUsIHZhbHVlKQoKY29yLnRlc3QoZ2RfZGZfMiRleWV0cl92YWx1ZSwgZ2RfZGZfMiRtb3RyX3ZhbHVlKQpgYGAKCmBgYHtyfQpnZF9kZl8zID0gcHJvdm9fZGYgJT4lIGZpbHRlcihtZXRyaWMgPT0gImdhemVfZHVyYXRpb24iKSAlPiUgc3ByZWFkKG1lYXN1cmUsIHZhbHVlKQoKY29yLnRlc3QoZ2RfZGZfMyRleWV0cl92YWx1ZSwgZ2RfZGZfMyRtb3RyX3ZhbHVlKQpgYGAKCgpgYGB7cn0KZ2RfZGZfNCA9IHByb3ZvX2RmICU+JSBmaWx0ZXIobWV0cmljID09ICJnb19wYXN0X3RpbWUiKSAlPiUgc3ByZWFkKG1lYXN1cmUsIHZhbHVlKQoKY29yLnRlc3QoZ2RfZGZfNCRleWV0cl92YWx1ZSwgZ2RfZGZfNCRtb3RyX3ZhbHVlKQoKYGBgCmBgYHtyfQpwcm92b19kZiAlPiUKICBnYXRoZXIod29yZF9wcm9wLCB3b3JkX3Byb3BfdmFsLCBjKCJmcmVxIiwgImxlbiIsICJzdXJwIikpICU+JQogIGZpbHRlcihtZXRyaWMgPT0gImdhemVfZHVyYXRpb24iKSAlPiUKICBnZ3Bsb3QoYWVzKHggPSB2YWx1ZSwgeT13b3JkX3Byb3BfdmFsLCBjb2xvciA9IG1lYXN1cmUpKSArCiAgICBnZW9tX3BvaW50KGFscGhhID0gMC4yKSArCiAgICBmYWNldF9ncmlkKHdvcmRfcHJvcH5tZWFzdXJlLCBzY2FsZXM9ImZyZWUiKSArCiAgICBnZW9tX3Ntb290aCgpICsKICAgIHhsYWIoIlJlYWRpbmcgTWVhc3VyZSIpCgojIGdnc2F2ZSgiLi4vdmlzdWFsaXphdGlvbi93b3JkX3Byb3BfY29tcHMucG5nIiwgZGV2aWNlID0gInBuZyIsIHdpZHRoID0gNiwgaGVpZ2h0ID0gMykKYGBgCgpgYGB7cn0KcHJvdm9fZGYgJT4lCiAgZ2dwbG90KGFlcyh4ID0gdmFsdWUsIHk9ZnJlcSwgY29sb3I9bWV0cmljKSkgKwogICAgZ2VvbV9wb2ludChhbHBoYSA9IDAuMikgKwogICAgZmFjZXRfZ3JpZChtZXRyaWN+bWVhc3VyZSwgc2NhbGVzPSJmcmVlIikgKwogICAgZ2VvbV9zbW9vdGgoKQpgYGAKCmBgYHtyfQpwcm92b19kZiAlPiUKICBnZ3Bsb3QoYWVzKHggPSB2YWx1ZSwgeT1zdXJwLCBjb2xvcj1tZXRyaWMpKSArCiAgICBnZW9tX3BvaW50KGFscGhhID0gMC4yKSArCiAgICBmYWNldF9ncmlkKG1ldHJpY35tZWFzdXJlLCBzY2FsZXM9ImZyZWUiKSArCiAgICBnZW9tX3Ntb290aCgpCmBgYAoKCgojIyBTaGFwZSBvZiBzdXJwcmlzYWwgLyBSVCByZWxhdGlvbnNoaXAKCmBgYHtyfQoKZml0X2dhbV9pbm5lciA9IGZ1bmN0aW9uKGJvb3RzdHJhcF9zYW1wbGUsIG1lYW5fcHJlZGljdG9ycykgewogIAogIGRmID0gYm9vdHN0cmFwX3NhbXBsZSRkYXRhCiAgd2VpZ2h0cyA9IHRhYnVsYXRlKGFzLmludGVnZXIoYm9vdHN0cmFwX3NhbXBsZSksIG5yb3coZGYpKQogIAogIG0gPSBnYW0ocHN5Y2hvbWV0cmljIH4gcyhzdXJwLCBicyA9ICdjcicsIGsgPSA2KSArIHMocHJldl9zdXJwLCBicyA9ICdjcicsIGsgPSA2KSArIHRlKGZyZXEsIGxlbiwgYnMgPSAnY3InKSArIHRlKHByZXZfZnJlcSwgcHJldl9sZW4sIGJzID0gJ2NyJyksIGRhdGEgPSBkZiwgd2VpZ2h0cyA9IHdlaWdodHMpCiAgdGVybXNfdG9fcHJlZGljdCA9IGMoInMoc3VycCkiLCAicyhwcmV2X3N1cnApIikKICAKICBuZXdkYXRhID0gZGF0YS5mcmFtZShzdXJwPXNlcSgwLDIwLGJ5PTAuMSksIHByZXZfc3VycD1tZWFuX3ByZWRpY3RvcnMkc3VycCwKICAgICAgICAgICAgICAgICAgICAgICBmcmVxPW1lYW5fcHJlZGljdG9ycyRmcmVxLCBwcmV2X2ZyZXE9bWVhbl9wcmVkaWN0b3JzJGZyZXEsCiAgICAgICAgICAgICAgICAgICAgICAgbGVuPW1lYW5fcHJlZGljdG9ycyRmcmVxLCBwcmV2X2xlbj1tZWFuX3ByZWRpY3RvcnMkZnJlcSkKCiAgIyBSZXR1cm5zIGEgbWF0cml4IE5fc2FtcGxlcyAqIE5fdGVybXMuCiAgcGVyX3Rlcm1fcHJlZGljdGlvbnMgPSBwcmVkaWN0KG0sIG5ld2RhdGE9bmV3ZGF0YSwgdGVybXM9dGVybXNfdG9fcHJlZGljdCwgdHlwZT0idGVybXMiKQoKICAjIEFkZGl0aXZlIG1vZGVsIC0tIHN1bSBhY3Jvc3MgcHJlZGljdG9yIHJlc3BvbnNlIGNvbnRyaWJ1dGlvbnMgKG1hdHJpeCBjb2x1bW5zKS4KICBwcmVkaWN0aW9ucyA9IHJvd1N1bXMocGVyX3Rlcm1fcHJlZGljdGlvbnMpCgogIHJldHVybihuZXdkYXRhICU+JSBtdXRhdGUoeT1wcmVkaWN0aW9ucykpCn0KCmZpdF9nYW0gPSBmdW5jdGlvbihkZiwgbWVhbl9wcmVkaWN0b3JzLCBhbHBoYT0wLjA1KSB7CiAgIyBCb290c3RyYXAtcmVzYW1wbGUgZGF0YQogIGJvb3RfbW9kZWxzID0gZGYgJT4lIGJvb3RzdHJhcHModGltZXM9MTApICU+JSAKICAgIyBGaXQgYSBHQU0gYW5kIGdldCBwcmVkaWN0aW9ucyBmb3IgZWFjaCBzYW1wbGUKICAgIG11dGF0ZShzbW9vdGhlZD1tYXAoc3BsaXRzLCBmaXRfZ2FtX2lubmVyLCBtZWFuX3ByZWRpY3RvcnM9bWVhbl9wcmVkaWN0b3JzKSkKICAKICAjIEV4dHJhY3QgbWVhbiBhbmQgNSUgYW5kIDk1JSBwZXJjZW50aWxlIHktdmFsdWVzIGZvciBlYWNoIHN1cnByaXNhbCB2YWx1ZQogIHJlc3VsdCA9IGJvb3RfbW9kZWxzICU+JSAKICAgIHVubmVzdChzbW9vdGhlZCkgJT4lIAogICAgZHBseXI6OnNlbGVjdChzdXJwLCB5KSAlPiUgCiAgICBncm91cF9ieShzdXJwKSAlPiUgCiAgICAgIHN1bW1hcmlzZSh5X2xvd2VyPXF1YW50aWxlKHksIGFscGhhIC8gMiksIAogICAgICAgICAgICAgICAgeV91cHBlcj1xdWFudGlsZSh5LCAxIC0gYWxwaGEgLyAyKSwKICAgICAgICAgICAgICAgIHk9bWVhbih5KSkgJT4lIAogICAgdW5ncm91cCgpCiAgCiAgcmV0dXJuIChyZXN1bHQpCn0KCgpgYGAKCgpgYGB7cn0KCmdhbV9tb2RlbGluZ19kZiA9IHByb3ZvX2RmICU+JQogIHNwcmVhZChtZWFzdXJlLCB2YWx1ZSkgJT4lCiAgbXV0YXRlKGxlbiA9IG5jaGFyKHdvcmQpKSAlPiUKICBncm91cF9ieShtZXRyaWMsIHRleHRfaWQpICU+JQogICAgYXJyYW5nZSh3b3JkX3RleHRfaWR4KSAlPiUKICAgIG11dGF0ZShwcmV2X3N1cnAgPSBsYWcoc3VycCksCiAgICAgICAgICAgcHJldl9mcmVxID0gbGFnKGZyZXEpLAogICAgICAgICAgIHByZXZfbGVuID0gbGFnKGxlbiksCiAgICAgICAgICAgcHJldl9leWV0cl92YWx1ZSA9IGxhZyhleWV0cl92YWx1ZSkpICU+JQogIHVuZ3JvdXAoKSAlPiUKICBkcm9wX25hKCkgJT4lCiAgcmVuYW1lKHBzeWNob21ldHJpYyA9IG1vdHJfdmFsdWUpCgoKc21vb3Roc19kZiA9IGRhdGEuZnJhbWUoKQoKbWV0cmljcyA9IGMoImdhemVfZHVyYXRpb24iLCAidG90YWxfZHVyYXRpb24iLCAiZ29fcGFzdF90aW1lIiwgImZpcnN0X2R1cmF0aW9uIikKZm9yIChtIGluIG1ldHJpY3MpIHsKICBwcmludChwYXN0ZTAoIkZpdHRpbmcgbW9kZWwgZm9yICIsIG0pKQogIGR1bW15X2RmID0gZ2FtX21vZGVsaW5nX2RmICU+JSBmaWx0ZXIobWV0cmljID09IG0pCiAgbWVhbl9wcmVkaWN0b3JzID0gZHVtbXlfZGYgJT4lIHN1bW1hcmlzZShzdXJwID0gbWVhbihzdXJwKSwgbGVuID0gbWVhbihsZW4pLCBmcmVxID0gbWVhbihmcmVxKSkKICBzbW9vdGhzID0gZHVtbXlfZGYgJT4lIGZpdF9nYW0oLiwgbWVhbl9wcmVkaWN0b3JzKQogICNGaXggMCBzdXJwcmlzYWwgPSAwIG1zCiAgZ2FtX3Ntb290aHMgPSBzbW9vdGhzICU+JSBtdXRhdGUoZGVsdGEgPSAwIC0geVsxXSwgeT15ICsgZGVsdGEsIHlfbG93ZXI9IHlfbG93ZXIgKyBkZWx0YSwgeV91cHBlcj15X3VwcGVyICsgZGVsdGEpCiAgc21vb3Roc19kZiA9IHJiaW5kKHNtb290aHNfZGYsIGdhbV9zbW9vdGhzICU+JSBtdXRhdGUocHN5Y2hvbWV0cmljID0gbSkpCn0KCmBgYAoKCmBgYHtyfQoKIyBTdXJwcmlzYWwgY3VydmVzCiAgZ2dwbG90KCkgKwogICAgICBnZW9tX2xpbmUoZGF0YSA9IHNtb290aHNfZGYsIGFlcyh4PXN1cnAsIHk9eSwgY29sb3IgPSBwc3ljaG9tZXRyaWMpLCBzaXplPTAuNykgKwogICAgICBnZW9tX3JpYmJvbihkYXRhID0gc21vb3Roc19kZiwgYWVzKHg9c3VycCwgeW1pbj15X2xvd2VyLCB5bWF4PXlfdXBwZXIsIGZpbGwgPSBwc3ljaG9tZXRyaWMpLCBhbHBoYT0wLjMsIHNpemU9MC41KSArCiAgICAgIHNjYWxlX3hfY29udGludW91cyhsYWJlbHM9YygwLCAxMCwgMjApLCBicmVha3M9YygwLCAxMCwgMjApLCBtaW5vcl9icmVha3MgPSBOVUxMKSArCiAgICAgIGZhY2V0X3dyYXAocHN5Y2hvbWV0cmljfi4pICsKICAgICAgeWxhYigiU2xvd2Rvd24gZHVlIHRvIFN1cnByaXNhbCAobXMpIikgKwogICAgICB4bGFiKCJTdXJwcmlzYWwgb2YgV29yZCIpICsKICAgICAgZ2d0aXRsZSgiUmVsYXRpb25zaGlwIGJldHdlZW4gTW9UUiBUaW1lcyBhbmQgU3VycHJpc2FsIikKICB0aGVtZSgKICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiwKICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCkKICApCgoKCgpgYGAKCiMgVGFyZ2V0ZWQgRXZhbHVhdGlvbiBEYXRhCgoKCmBgYHtyfQoKbW90cl9hdHRhY2hfZGYgPSBtb3RyX2RmICU+JQogIGZpbHRlcihleHByX2lkID09ICJBdHRhY2htZW50IikgJT4lCiAgcmVuYW1lKCBpdGVtX2lkID0gcGFyYV9ucikgJT4lCiAgbXV0YXRlKGl0ZW1faWQgPSBhcy5pbnRlZ2VyKGl0ZW1faWQpKSAlPiUKICBtdXRhdGUoY29uZF9pZCA9IGFzLmZhY3Rvcihjb25kX2lkKSkgJT4lCiAgbXV0YXRlKGNvbmRfaWQgPSBpZl9lbHNlKGNvbmRfaWQgPT0gMSwgIk5vIENvbW1hIiwgIkNvbW1hIikpICU+JQogIGZpbHRlcihtZXRyaWMgIT0gIkZQUmVnIikgJT4lCgogIGZpbHRlcighIChpdGVtX2lkID09IDQgJiBjb25kX2lkID09ICJObyBDb21tYSIpICkgJT4lICMganVzdCBiZWNhdXNlIG9mIGFsaWdubWVudCBpc3N1ZXMgZm9yIG5vdwogIAogIG11dGF0ZShjcml0ID0gaWZfZWxzZSh3b3JkID09ICJhbmQiLCB3b3JkX25yLCBhcy5pbnRlZ2VyKDApICkpICU+JQogIGdyb3VwX2J5KGNvbmRfaWQsIGl0ZW1faWQpICU+JQogICAgbXV0YXRlKGNyaXQgPSB1bmlxdWUoY3JpdClbMl0pICU+JQogIHVuZ3JvdXAoKSAlPiUKICBtdXRhdGUod29yZF9uciA9IHdvcmRfbnIgLSBjcml0KQoKCmFnZ19tb3RyX2F0dGFjaF9kZiA9IG1vdHJfYXR0YWNoX2RmICU+JQogIGZpbHRlcih3b3JkX25yID49IC0yLCB3b3JkX25yIDwgNikgJT4lCiAgZ3JvdXBfYnkoY29uZF9pZCwgd29yZF9uciwgbWV0cmljKSAlPiUKICAgIHN1bW1hcmlzZSggbSA9IG1lYW4odmFsdWUpLAogICAgICAgICAgICAgICBzZCA9IHN0ZC5lcnJvcih2YWx1ZSksCiAgICAgICAgICAgICAgIHVwcGVyID0gbSArIDEuOTYgKiBzZCwKICAgICAgICAgICAgICAgbG93ZXIgPSBtIC0gMS45OCAqIHNkLAogICAgICAgICAgICAgICBuID0gbigpKSAlPiUKICB1bmdyb3VwKCkKCmFnZ19tb3RyX2F0dGFjaF9kZiAlPiUKICBnZ3Bsb3QoYWVzKHggPSB3b3JkX25yLCB5ID0gbSwgY29sb3IgPSBjb25kX2lkKSkgKwogICAgZ2VvbV9yZWN0KGFlcyh4bWluID0gMi41LCB4bWF4ID0gNS41LCB5bWluID0gMTAwLCB5bWF4ID0gODAwKSwgZmlsbD1hbHBoYSgid2hpdGUiLCAwKSwgY29sb3IgPSAiIzQ1ZWY3MCIsIGxpbmV0eXBlID0gImRvdHRlZCIpICsKICAgIGdlb21fcG9pbnQoKSArCiAgICBnZW9tX2Vycm9yYmFyKGFlcyh5bWF4ID0gdXBwZXIsIHltaW4gPSBsb3dlciksIHdpZHRoID0gMC4zKSArCiAgICBnZW9tX2xpbmUoKSArCiAgICAjZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IHdvcmQsIHkgPSBpZl9lbHNlKGNvbmRfaWQgPT0gIkNvbW1hIiwgMzAwMCwgMzUwMCkpLCBzaXplID0gMikgKwogICAgI2ZhY2V0X2dyaWQocGFyYV9ucn5jb25kX2lkKSArCiAgeWxhYigiUmVhZGluZyBUaW1lIikgKwogIHhsYWIoIkNvbmRpdGlvbiIpICsKICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzPS0yOjUsIGxhYmVscz1jKCJ0aGUiLCAibWFuIiwgImFuZCIsICJoaXMiLCAid2lmZSIsICJyYW4iLCAiYXdheSIsICJmcm9tIikpICsKICBmYWNldF9ncmlkKH5tZXRyaWMpICsKICB0aGVtZSgKICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iLAogICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgaGp1c3QgPSAxKQogICkKCiNnZ3NhdmUoIi4uL3Zpc3VhbGl6YXRpb24vYXR0YWNobWVudC5wbmciLCBkZXZpY2UgPSAicG5nIiwgd2lkdGggPSA2LCBoZWlnaHQgPSAzKQoKYGBgCgpgYGB7cn0KCm9wdGlvbnMoSlVMSUFfSE9NRSA9ICIvQXBwbGljYXRpb25zL0p1bGlhLTEuOC5hcHAvQ29udGVudHMvUmVzb3VyY2VzL2p1bGlhL2Jpbi8iKQojIGxpYnJhcnkoamdsbW0pCiMgamdsbW1fc2V0dXAoKQoKYXR0YWNoX2xtX2RmID0gbW90cl9hdHRhY2hfZGYgJT4lCiAgZmlsdGVyKG1ldHJpYyA9PSAiZ2F6ZV9kdXJhdGlvbiIpICU+JQogIGZpbHRlcih3b3JkX25yID09IDMpICU+JQogIG11dGF0ZShpdGVtX2lkID0gYXMuZmFjdG9yKGl0ZW1faWQpLAogICAgICAgICBzdWJqID0gYXMuZmFjdG9yKHN1YmopKQoKbSA9IGF0dGFjaF9sbV9kZiAlPiUKICBsbWVyKHZhbHVlIH4gY29uZF9pZCArIChjb25kX2lkIHwgaXRlbV9pZCkgKyAoY29uZF9pZCB8IHN1YmopLCBkYXRhPS4pCgpzdW1tYXJ5KG0pCgpgYGAKCgoKCg==